/*
 *    Copyright (C) 2012 Hai Bison
 *
 *    See the file LICENSE at the root directory of this project for copying
 *    permission.
 */

package group.pals.desktop.app.apksigner;

import group.pals.desktop.app.apksigner.i18n.Messages;
import group.pals.desktop.app.apksigner.i18n.R;
import group.pals.desktop.app.apksigner.utils.Files;
import group.pals.desktop.app.apksigner.utils.KeyGen;
import group.pals.desktop.app.apksigner.utils.Preferences;
import group.pals.desktop.app.apksigner.utils.Texts;
import group.pals.desktop.app.apksigner.utils.ui.Dlg;
import group.pals.desktop.app.apksigner.utils.ui.JEditorPopupMenu;
import group.pals.desktop.app.apksigner.utils.ui.UI;

import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.TitledBorder;
import javax.swing.text.JTextComponent;

/**
 * Keystore file generator panel.
 * 
 * @author Hai Bison
 * @since v1.6 beta
 * 
 */
public class PanelKeyGen extends JPanel {

    /**
     * Auto-generated by Eclipse.
     */
    private static final long serialVersionUID = -6571727138994044544L;

    /**
     * The class name.
     */
    private static final String CLASSNAME = PanelKeyGen.class.getName();

    /**
     * This key holds the last working directory.
     */
    private static final String PKEY_LAST_WORKING_DIR = CLASSNAME
            + ".last_working_dir";

    /*
     * FIELDS
     */

    private File mTargetFile;

    /*
     * CONTROLS
     */

    private JPasswordField mTextPassword;
    private JPasswordField mTextPasswordConfirm;
    private JTextField mTextAlias;
    private JPasswordField mTextAliasPassword;
    private JPasswordField mTextAliasPasswordConfirm;
    private JSpinner mSpinnerValidity;
    private JTextField mTextName;
    private JTextField mTextOrganizationalUnit;
    private JTextField mTextOrganization;
    private JTextField mTextCityOrLocality;
    private JTextField mTextStateOrProvince;
    private JTextField mTextCountryCode;
    private JPanel panel;
    private JButton mBtnChooseTargetFile;
    private JButton mBtnGenerateKeyfile;

    /**
     * Create the panel.
     */
    public PanelKeyGen() {
        GridBagLayout gridBagLayout = new GridBagLayout();
        gridBagLayout.columnWidths = new int[] { 0, 0, 0 };
        gridBagLayout.rowHeights = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        gridBagLayout.columnWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE };
        gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                0.0, 0.0, Double.MIN_VALUE };
        setLayout(gridBagLayout);

        panel = new JPanel();
        panel.setBorder(new TitledBorder(null, Messages
                .getString(R.string.target_file), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_panel = new GridBagConstraints();
        gbc_panel.gridwidth = 2;
        gbc_panel.insets = new Insets(10, 10, 10, 10);
        gbc_panel.fill = GridBagConstraints.HORIZONTAL;
        gbc_panel.gridx = 0;
        gbc_panel.gridy = 0;
        add(panel, gbc_panel);
        panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        mBtnChooseTargetFile = new JButton(
                Messages.getString(R.string.desc_save_as));
        mBtnChooseTargetFile
                .addActionListener(mBtnChooseTargetFileActionListener);
        panel.add(mBtnChooseTargetFile);

        mBtnGenerateKeyfile = new JButton(
                Messages.getString(R.string.generate_keyfile)); //$NON-NLS-1$
        mBtnGenerateKeyfile
                .addActionListener(mBtnGenerateKeyfileActionListener);
        panel.add(mBtnGenerateKeyfile);

        mTextPassword = new JPasswordField();
        mTextPassword.setHorizontalAlignment(SwingConstants.CENTER);
        mTextPassword.setBorder(new TitledBorder(null, Messages
                .getString(R.string.password), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextPassword = new GridBagConstraints();
        gbc_mTextPassword.insets = new Insets(3, 3, 3, 3);
        gbc_mTextPassword.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextPassword.gridx = 0;
        gbc_mTextPassword.gridy = 1;
        add(mTextPassword, gbc_mTextPassword);

        mTextPasswordConfirm = new JPasswordField();
        mTextPasswordConfirm.setHorizontalAlignment(SwingConstants.CENTER);
        mTextPasswordConfirm.setBorder(new TitledBorder(null, Messages
                .getString(R.string.confirm), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextPasswordConfirm = new GridBagConstraints();
        gbc_mTextPasswordConfirm.insets = new Insets(3, 3, 3, 3);
        gbc_mTextPasswordConfirm.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextPasswordConfirm.gridx = 1;
        gbc_mTextPasswordConfirm.gridy = 1;
        add(mTextPasswordConfirm, gbc_mTextPasswordConfirm);

        mTextAlias = new JTextField();
        mTextAlias.setHorizontalAlignment(SwingConstants.CENTER);
        mTextAlias.setBorder(new TitledBorder(null, Messages
                .getString(R.string.alias), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextAlias = new GridBagConstraints();
        gbc_mTextAlias.insets = new Insets(3, 3, 3, 3);
        gbc_mTextAlias.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextAlias.gridx = 0;
        gbc_mTextAlias.gridy = 2;
        add(mTextAlias, gbc_mTextAlias);
        mTextAlias.setColumns(10);

        mTextAliasPassword = new JPasswordField();
        mTextAliasPassword.setHorizontalAlignment(SwingConstants.CENTER);
        mTextAliasPassword.setBorder(new TitledBorder(null, Messages
                .getString(R.string.alias_password), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextAliasPassword = new GridBagConstraints();
        gbc_mTextAliasPassword.insets = new Insets(3, 3, 3, 3);
        gbc_mTextAliasPassword.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextAliasPassword.gridx = 0;
        gbc_mTextAliasPassword.gridy = 3;
        add(mTextAliasPassword, gbc_mTextAliasPassword);

        mTextAliasPasswordConfirm = new JPasswordField();
        mTextAliasPasswordConfirm.setHorizontalAlignment(SwingConstants.CENTER);
        mTextAliasPasswordConfirm.setBorder(new TitledBorder(null, Messages
                .getString(R.string.confirm), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextAliasPasswordConfirm = new GridBagConstraints();
        gbc_mTextAliasPasswordConfirm.insets = new Insets(3, 3, 3, 3);
        gbc_mTextAliasPasswordConfirm.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextAliasPasswordConfirm.gridx = 1;
        gbc_mTextAliasPasswordConfirm.gridy = 3;
        add(mTextAliasPasswordConfirm, gbc_mTextAliasPasswordConfirm);

        mSpinnerValidity = new JSpinner();
        mSpinnerValidity.setBorder(new TitledBorder(null, Messages
                .getString(R.string.validity), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        mSpinnerValidity.setValue(25);
        GridBagConstraints gbc_mSpinnerValidity = new GridBagConstraints();
        gbc_mSpinnerValidity.fill = GridBagConstraints.HORIZONTAL;
        gbc_mSpinnerValidity.insets = new Insets(3, 3, 3, 3);
        gbc_mSpinnerValidity.gridx = 0;
        gbc_mSpinnerValidity.gridy = 4;
        add(mSpinnerValidity, gbc_mSpinnerValidity);

        mTextName = new JTextField();
        mTextName.setHorizontalAlignment(SwingConstants.CENTER);
        mTextName.setBorder(new TitledBorder(null, Messages
                .getString(R.string.first_and_last_name), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextName = new GridBagConstraints();
        gbc_mTextName.insets = new Insets(3, 3, 3, 3);
        gbc_mTextName.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextName.gridx = 0;
        gbc_mTextName.gridy = 5;
        add(mTextName, gbc_mTextName);
        mTextName.setColumns(10);

        mTextOrganizationalUnit = new JTextField();
        mTextOrganizationalUnit.setHorizontalAlignment(SwingConstants.CENTER);
        mTextOrganizationalUnit.setBorder(new TitledBorder(null, Messages
                .getString(R.string.organizational_unit), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextOrganizationalUnit = new GridBagConstraints();
        gbc_mTextOrganizationalUnit.insets = new Insets(3, 3, 3, 3);
        gbc_mTextOrganizationalUnit.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextOrganizationalUnit.gridx = 1;
        gbc_mTextOrganizationalUnit.gridy = 5;
        add(mTextOrganizationalUnit, gbc_mTextOrganizationalUnit);
        mTextOrganizationalUnit.setColumns(10);

        mTextOrganization = new JTextField();
        mTextOrganization.setHorizontalAlignment(SwingConstants.CENTER);
        mTextOrganization.setBorder(new TitledBorder(null, Messages
                .getString(R.string.organization), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextOrganization = new GridBagConstraints();
        gbc_mTextOrganization.insets = new Insets(3, 3, 3, 3);
        gbc_mTextOrganization.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextOrganization.gridx = 0;
        gbc_mTextOrganization.gridy = 6;
        add(mTextOrganization, gbc_mTextOrganization);
        mTextOrganization.setColumns(10);

        mTextCityOrLocality = new JTextField();
        mTextCityOrLocality.setHorizontalAlignment(SwingConstants.CENTER);
        mTextCityOrLocality.setBorder(new TitledBorder(null, Messages
                .getString(R.string.city_or_locality), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextCityOrLocality = new GridBagConstraints();
        gbc_mTextCityOrLocality.insets = new Insets(3, 3, 3, 3);
        gbc_mTextCityOrLocality.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextCityOrLocality.gridx = 1;
        gbc_mTextCityOrLocality.gridy = 6;
        add(mTextCityOrLocality, gbc_mTextCityOrLocality);
        mTextCityOrLocality.setColumns(10);

        mTextStateOrProvince = new JTextField();
        mTextStateOrProvince.setHorizontalAlignment(SwingConstants.CENTER);
        mTextStateOrProvince.setBorder(new TitledBorder(null, Messages
                .getString(R.string.state_or_province), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextStateOrProvince = new GridBagConstraints();
        gbc_mTextStateOrProvince.insets = new Insets(3, 3, 3, 3);
        gbc_mTextStateOrProvince.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextStateOrProvince.gridx = 0;
        gbc_mTextStateOrProvince.gridy = 7;
        add(mTextStateOrProvince, gbc_mTextStateOrProvince);
        mTextStateOrProvince.setColumns(10);

        mTextCountryCode = new JTextField();
        mTextCountryCode.setHorizontalAlignment(SwingConstants.CENTER);
        mTextCountryCode.setBorder(new TitledBorder(null, Messages
                .getString(R.string.country_code), TitledBorder.LEADING,
                TitledBorder.TOP, null, null));
        GridBagConstraints gbc_mTextCountryCode = new GridBagConstraints();
        gbc_mTextCountryCode.insets = new Insets(3, 3, 3, 3);
        gbc_mTextCountryCode.fill = GridBagConstraints.HORIZONTAL;
        gbc_mTextCountryCode.gridx = 1;
        gbc_mTextCountryCode.gridy = 7;
        add(mTextCountryCode, gbc_mTextCountryCode);
        mTextCountryCode.setColumns(10);

        JEditorPopupMenu.apply(this);
    }// PanelKeyGen()

    /**
     * Validates all fields.
     * 
     * @return {@code true} or {@ode false}.
     */
    private boolean validateFields() {
        if (mTargetFile == null || !mTargetFile.getParentFile().isDirectory()) {
            Dlg.showInfoMsg(Messages
                    .getString(R.string.msg_specify_target_file));
            mBtnChooseTargetFile.requestFocus();
            return false;
        }

        if (mTextPassword.getPassword() == null
                || mTextPassword.getPassword().length == 0) {
            Dlg.showErrMsg(Messages.getString(R.string.msg_password_is_empty));
            mTextPassword.requestFocus();
            return false;
        }

        if (!Arrays.equals(mTextPassword.getPassword(),
                mTextPasswordConfirm.getPassword())) {
            Dlg.showErrMsg(Messages
                    .getString(R.string.msg_passwords_dont_match));
            mTextPassword.requestFocus();
            return false;
        }

        if (mTextAlias.getText() == null
                || mTextAlias.getText().trim().isEmpty()) {
            Dlg.showErrMsg(Messages.getString(R.string.msg_alias_is_empty));
            mTextAlias.requestFocus();
            return false;
        }

        if (mTextAliasPassword.getPassword() == null
                || mTextAliasPassword.getPassword().length == 0) {
            Dlg.showErrMsg(Messages
                    .getString(R.string.msg_alias_password_is_empty));
            mTextAliasPassword.requestFocus();
            return false;
        }

        if (!Arrays.equals(mTextAliasPassword.getPassword(),
                mTextAliasPasswordConfirm.getPassword())) {
            Dlg.showErrMsg(Messages
                    .getString(R.string.msg_alias_passwords_dont_match));
            mTextAliasPassword.requestFocus();
            return false;
        }

        if (!(mSpinnerValidity.getValue() instanceof Integer)
                || (Integer) mSpinnerValidity.getValue() <= 0) {
            Dlg.showErrMsg(Messages
                    .getString(R.string.msg_validity_must_be_larger_than_zero));
            mSpinnerValidity.requestFocus();
            return false;
        }

        boolean okay = false;
        for (JTextComponent comp : new JTextComponent[] { mTextName,
                mTextOrganizationalUnit, mTextOrganization,
                mTextCityOrLocality, mTextStateOrProvince, mTextCountryCode }) {
            if (comp.getText() != null && !comp.getText().trim().isEmpty()) {
                okay = true;
                break;
            }
        }
        if (!okay) {
            Dlg.showErrMsg(Messages
                    .getString(R.string.msg_at_least_one_certificate_issuer_field_is_required));
            mTextName.requestFocus();
            return false;
        }

        return true;
    }// validateFields()

    /**
     * Generates keyfile.
     * <p>
     * <b>Notes:</b> You should call {@link #validateFields()} first.
     * </p>
     */
    private void genKeyfile() {
        try {
            if (mTargetFile.exists()) {
                mTargetFile.delete();
            }

            KeyGen.genKey(Preferences.getInstance().getJdkPath(), mTargetFile,
                    mTextPassword.getPassword(), mTextAlias.getText().trim(),
                    mTextAliasPassword.getPassword(),
                    (Integer) mSpinnerValidity.getValue() * 365, mTextName
                            .getText().trim(), mTextOrganizationalUnit
                            .getText().trim(), mTextOrganization.getText()
                            .trim(), mTextCityOrLocality.getText().trim(),
                    mTextStateOrProvince.getText().trim(), mTextCountryCode
                            .getText().trim());
            Dlg.showInfoMsg(Messages
                    .getString(R.string.msg_keyfile_generated_successfully));
        } catch (Exception e) {
            Dlg.showErrMsg(Messages.getString(
                    R.string.pmsg_error_generating_keyfile, e));
        }
    }// genKeyfile()

    /*
     * LISTENERS
     */

    private final ActionListener mBtnChooseTargetFileActionListener = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            mTargetFile = Files.chooseFileToSave(new File(Preferences
                    .getInstance().get(PKEY_LAST_WORKING_DIR, "/")),
                    Texts.FILE_EXT_KEYSTORE, Texts.REGEX_KEYSTORE_FILES,
                    Messages.getString(R.string.desc_keystore_files));
            if (mTargetFile != null) {
                mBtnChooseTargetFile.setText(mTargetFile.getName());
                mBtnChooseTargetFile.setForeground(UI.COLOUR_SELECTED_FILE);
                Preferences.getInstance().set(PKEY_LAST_WORKING_DIR,
                        mTargetFile.getParentFile().getAbsolutePath());
                mTextPassword.requestFocus();
            } else {
                mBtnChooseTargetFile.setText(Messages
                        .getString(R.string.desc_save_as));
                mBtnChooseTargetFile.setForeground(UI.COLOUR_WAITING_CMD);
            }
        }// actionPerformed()
    };// mBtnChooseTargetFileActionListener

    private final ActionListener mBtnGenerateKeyfileActionListener = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (validateFields())
                genKeyfile();
        }// actionPerformed()
    };// mBtnGenerateKeyfileActionListener

}
